include( "player_class.lua" )
include( "player_extension.lua" )
include( "class_default.lua" )
include( "player_colours.lua" )

fretta_voting = CreateConVar( "fretta_voting", "1", { FCVAR_REPLICATED, FCVAR_NOTIFY, FCVAR_ARCHIVE } )

GM.Name 	= "Frettabox Base"
GM.Author 	= "TEAM GARRY + Entoros"
GM.Email 	= ""
GM.Website 	= "www.garrysmod.com"
GM.Help		= "No Help Available"

GM.TeamBased = false					// Team based game or a Free For All game?
GM.AllowAutoTeam = true
GM.AllowSpectating = true
GM.SecondsBetweenTeamSwitches = 10
GM.GameLength = 60
GM.RoundLimit = -1					// Maximum amount of rounds to be played in round based games
GM.VotingDelay = 5					// Delay between end of game, and vote. if you want to display any extra screens before the vote pops up

GM.NoPlayerSuicide = false
GM.NoPlayerDamage = false
GM.NoPlayerSelfDamage = false		// Allow players to hurt themselves?
GM.NoPlayerTeamDamage = true		// Allow team-members to hurt each other?
GM.NoPlayerPlayerDamage = false 	// Allow players to hurt each other?
GM.NoNonPlayerPlayerDamage = false 	// Allow damage from non players (physics, fire etc)
GM.NoPlayerFootsteps = false		// When true, all players have silent footsteps
GM.PlayerCanNoClip = false			// When true, players can use noclip without sv_cheats
GM.TakeFragOnSuicide = true			// -1 frag on suicide

GM.MaximumDeathLength = 0			// Player will repspawn if death length > this (can be 0 to disable)
GM.MinimumDeathLength = 2			// Player has to be dead for at least this long
GM.AutomaticTeamBalance = false     // Teams will be periodically balanced 
GM.ForceJoinBalancedTeams = true	// Players won't be allowed to join a team if it has more players than another team
GM.RealisticFallDamage = false
GM.AddFragsToTeamScore = false		// Adds player's individual kills to team score (must be team based)

GM.NoAutomaticSpawning = false		// Players don't spawn automatically when they die, some other system spawns them
GM.RoundBased = false				// Round based, like CS
GM.RoundLength = 30					// Round length, in seconds
GM.RoundPreStartTime = 5			// Preperation time before a round starts
GM.RoundPostLength = 8				// Seconds to show the 'x team won!' screen at the end of a round
GM.RoundEndsWhenOneTeamAlive = true	// CS Style rules

GM.EnableFreezeCam = false			// TF2 Style Freezecam
GM.DeathLingerTime = 4				// The time between you dying and it going into spectator mode, 0 disables

GM.SelectModel = true               // Can players use the playermodel picker in the F1 menu?
GM.SelectColor = false				// Can players modify the colour of their name? (ie.. no teams)

GM.PlayerRingSize = 48              // How big are the colored rings under the player's feet (if they are enabled) ?
GM.HudSkin = "SimpleSkin"
GM.SuicideString = "died"
GM.DeathNoticeDefaultColor = Color( 255, 128, 0 ); // Default colour for entity kills
GM.DeathNoticeTextColor = color_white; // colour for text ie. "died", "killed"

GM.ValidSpectatorModes = { OBS_MODE_CHASE, OBS_MODE_IN_EYE, OBS_MODE_ROAMING }
GM.ValidSpectatorEntities = { "player" }	// Entities we can spectate
GM.CanOnlySpectateOwnTeam = true; // you can only spectate players on your own team
GM.PhysLimited = true	// Can't pickup everything with physgun

DeriveGamemode( "base" )

TEAM_PLAYER = 1

function GM:CreateTeams()
	
	team.SetUp( TEAM_PLAYER, "Players", Color( 255, 255, 255 ), true )
	team.SetSpawnPoint( TEAM_PLAYER, { "info_player_start", "info_player_terrorist", "info_player_counterterrorist" } )
	team.SetClass( TEAM_HUMANS, { "Default" } )
	
	team.SetUp( TEAM_SPECTATOR, "Spectators", Color( 200, 200, 200 ), true )
	team.SetSpawnPoint( TEAM_SPECTATOR, { "info_player_start", "info_player_terrorist", "info_player_counterterrorist" } )
	
end

function GM:InGamemodeVote()
	return GetGlobalBool( "InGamemodeVote", false )
end

function GM:TeamHasEnoughPlayers( teamid )

	local PlayerCount = team.NumPlayers( teamid )

	// Don't let them join a team if it has more players than another team
	if ( GAMEMODE.ForceJoinBalancedTeams ) then
	
		for id, tm in pairs( team.GetAllTeams() ) do
			if ( id > 0 && id < 1000 && team.NumPlayers( id ) < PlayerCount && team.Joinable(id) ) then return true end
		end
		
	end

	return false
	
end

function GM:PlayerCanJoinTeam( ply, teamid )

	if ( SERVER && !self.BaseClass:PlayerCanJoinTeam( ply, teamid ) ) then 
		return false 
	end

	if ( GAMEMODE:TeamHasEnoughPlayers( teamid ) ) then
		ply:ChatPrint( "That team is full!" )
		return false
	end
	
	return true
	
end


function GM:Move( ply, mv )

	if ( ply:CallClassFunction( "Move", mv ) ) then return true end

end


function GM:KeyPress( ply, key )

	if ( ply:CallClassFunction( "OnKeyPress", key ) ) then return true end

end


function GM:KeyRelease( ply, key )

	if ( ply:CallClassFunction( "OnKeyRelease", key ) ) then return true end

end

function GM:PlayerFootstep( ply, pos, foot, sound, volume, rf ) 

	if( GAMEMODE.NoPlayerFootsteps || !ply:Alive() || ply:Team() == TEAM_SPECTATOR || ply:IsObserver() ) then
		return true;
	end
	
	local Class = ply:GetPlayerClass();
	if( !Class ) then return end
	
	if( Class.DisableFootsteps ) then // rather than using a hook, we can just do this to override the function instead.
		return true;
	end
	
	if( Class.Footstep ) then
		return Class:Footstep( ply, pos, foot, sound, volume, rf ); // Call footstep function in class, you can use this to make custom footstep sounds
	end
	
end

function GM:CalcView( ply, origin, angles, fov )

	local view = ply:CallClassFunction( "CalcView", origin, angles, fov )
	
	view = view or {}
	
	local wep = ply:GetActiveWeapon()
	if ( ValidEntity( wep ) ) then
	
		origin = view.origin or origin
		angles = view.angles or angles
		fov = view.fov or fov
	
		local func = wep.GetViewModelPosition
		if ( func ) then view.vm_origin,  view.vm_angles = func( wep, origin*1, angles*1 ) end
		
		local func = wep.CalcView
		if ( func ) then view.origin, view.angles, view.fov = func( wep, ply, origin*1, angles*1, fov ) end
	
	end

	return view
	
end

// You can override this to make it use a cvar instead, return in seconds
function GM:GetTimeLimit()

	if( GAMEMODE.GameLength > 0 ) then
		return GAMEMODE.GameLength * 60;
	end
	
	return -1;
	
end

function GM:GetGameTimeLeft()

	local EndTime = GAMEMODE:GetTimeLimit();
	if ( EndTime == -1 ) then return -1 end
	
	return EndTime - CurTime()

end

// This function includes /yourgamemode/player_class/*.lua
// And AddCSLuaFile's each of those files.
// You need to call it in your derived shared.lua IF you have files in that folder
// and want to include them!

function IncludePlayerClasses()

	local Folder = string.Replace( GM.Folder, "gamemodes/", "" );

	for c,d in pairs(file.FindInLua(Folder.."/gamemode/player_class/*.lua")) do
		include( Folder.."/gamemode/player_class/"..d )
		AddCSLuaFile( Folder.."/gamemode/player_class/"..d )
	end

end

IncludePlayerClasses()		

/*---------------------------------------------------------
   Name: gamemode:CanConstrain( ply, trace, mode )
   Return true if the player is allowed to do this constrain
---------------------------------------------------------*/
function GM:CanConstrain( ply, trace, mode )

	// Not allowed to constrain their fellow players
	if (trace.Entity:IsValid() && trace.Entity:IsPlayer()) then
		return false
	end
	
	// Give the entity a chance to decide
	if ( trace.Entity:GetTable().CanConstrain ) then
		return trace.Entity:GetTable():CanConstrain( ply, trace, mode )
	end
	
	//Msg( "Can Constrain "..mode.."\n" )

	return true
	
end

/*---------------------------------------------------------
   Name: gamemode:CanConstruct( ply, trace, mode )
   Return true if the player is allowed to do this construction
---------------------------------------------------------*/
function GM:CanConstruct( ply, trace, mode )

	if ( mode == "remover" && !trace.Entity:IsValid()) then
		return false
	end
	
	// The jeep spazzes out when applying something
	// todo: Find out what it's reacting badly to and change it in _physprops
	if ( mode == "physprop" && trace.Entity:IsValid() && trace.Entity:GetClass() == "prop_vehicle_jeep" ) then
		return false
	end
	
	// Give the entity a chance to decide
	if ( trace.Entity:GetTable().CanConstruct ) then
		return trace.Entity:GetTable():CanConstruct( ply, trace, mode )
	end

	return true
	
end

/*---------------------------------------------------------
   Name: gamemode:CanPose( ply, trace, mode )
   Return true if the player is allowed to do this constrain
---------------------------------------------------------*/
function GM:CanPose( ply, trace, mode )
	return true
end

/*---------------------------------------------------------
   Name: gamemode:CanRender( ply, trace, mode )
   Return true if the player is allowed to do this constrain
---------------------------------------------------------*/
function GM:CanRender( ply, trace, mode )
	return true
end

/*---------------------------------------------------------
   Name: gamemode:CanTool( ply, trace, mode )
   Return true if the player is allowed to use this tool
---------------------------------------------------------*/
function GM:CanTool( ply, trace, mode )

	// The jeep spazzes out when applying something
	// todo: Find out what it's reacting badly to and change it in _physprops
	if ( mode == "physprop" && trace.Entity:IsValid() && trace.Entity:GetClass() == "prop_vehicle_jeep" ) then
		return false
	end
	
	// If we have a toolsallowed table, check to make sure the toolmode is in it
	if ( trace.Entity.m_tblToolsAllowed ) then
	
		local vFound = false	
		for k, v in pairs( trace.Entity.m_tblToolsAllowed ) do
			if ( mode == v ) then vFound = true end
		end

		if ( !vFound ) then return false end

	end
	
	// Give the entity a chance to decide
	if ( trace.Entity.CanTool ) then
		return trace.Entity:CanTool( ply, trace, mode )
	end

	return true
	
end


/*---------------------------------------------------------
   Name: gamemode:GravGunPunt( )
   Desc: We're about to punt an entity (primary fire).
		 Return true if we're allowed to.
---------------------------------------------------------*/
function GM:GravGunPunt( ply, ent )

	if ( ent:IsValid() && ent:GetTable().GravGunPunt ) then
		return ent:GetTable():GravGunPunt( ply )
	end

	return self.BaseClass:GravGunPunt( ply, ent )
	
end

/*---------------------------------------------------------
   Name: gamemode:GravGunPickupAllowed( )
   Desc: Return true if we're allowed to pickup entity
---------------------------------------------------------*/
function GM:GravGunPickupAllowed( ply, ent )

	if ( ent:IsValid() && ent:GetTable().GravGunPickupAllowed ) then
		return ent:GetTable():GravGunPickupAllowed( ply )
	end

	return self.BaseClass:GravGunPickupAllowed( ply, ent )
	
end


/*---------------------------------------------------------
   Name: gamemode:PhysgunPickup( )
   Desc: Return true if player can pickup entity
---------------------------------------------------------*/
function GM:PhysgunPickup( ply, ent )

	if ( ent:IsValid() && ent.PhysgunPickup ) then
		return ent:PhysgunPickup( ply )
	end
	
	// Some entities specifically forbid physgun interaction
	if ( ent.PhysgunDisabled ) then return false end
	
	local EntClass = ent:GetClass()

	// Never pick up players
	if ( EntClass == "player" ) then return false end
	
	if ( PhysLimited == true ) then
	
		if ( string.find( EntClass, "prop_dynamic" ) ) then return false end
		if ( string.find( EntClass, "prop_door" ) ) then return false end
		
		// Don't move physboxes if the mapper logic says no
		if ( EntClass == "func_physbox" && ent:HasSpawnFlags( SF_PHYSBOX_MOTIONDISABLED ) ) then return false  end
		
		// If the physics object is frozen by the mapper, don't allow us to move it.
		if ( string.find( EntClass, "prop_" ) && ( ent:HasSpawnFlags( SF_PHYSPROP_MOTIONDISABLED ) || ent:HasSpawnFlags( SF_PHYSPROP_PREVENT_PICKUP ) ) ) then return false end
		
		// Allow physboxes, but get rid of all other func_'s (ladder etc)
		if ( EntClass != "func_physbox" && string.find( EntClass, "func_" ) ) then return false end

	
	end
	
	if ( SERVER ) then 
	
		ply:SendHint( "PhysgunFreeze", 2 )
		ply:SendHint( "PhysgunUse", 8 )
		
	end
	
	return true
	
end

/*---------------------------------------------------------
   Name: gamemode:EntityKeyValue( ent, key, value )
   Desc: Called when an entity has a keyvalue set
	      Returning a string it will override the value
---------------------------------------------------------*/
function GM:EntityKeyValue( ent, key, value )


	// Physgun not allowed on this prop..
	if ( key == "gmod_allowphysgun" && value == '0' ) then
		ent.PhysgunDisabled = true
	end

	// Prop has a list of tools that are allowed on it.
	if ( key == "gmod_allowtools" ) then
		ent.m_tblToolsAllowed = string.Explode( " ", value )
	end
	
end